﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ChatBackend.Common.Logs
{
    public class SerilogRequestLoggingMiddleware
    {
        private readonly RequestDelegate _next;

        /// <inheritdoc />
        public SerilogRequestLoggingMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        /// <summary>
        /// 读取请求内容
        /// </summary>
        private static async Task<string> ReadRequestBody(HttpRequest request)
        {
            if (request.Method == HttpMethods.Get)
            {
                return $"This is a {request.Method} request";
            }
            if (request.ContentType == null || !request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            {
                return "Request content is not in json format";
            }
            request.EnableBuffering();
            var body = request.Body;

            var buffer = new byte[Convert.ToInt32(request.ContentLength)];

            _ = await request.Body.ReadAsync(buffer, 0, buffer.Length);

            var bodyAsText = Encoding.UTF8.GetString(buffer);

            request.Body = body;
            request.Body.Seek(0, SeekOrigin.Begin);

            return bodyAsText;
        }

        /// <summary>
        /// 读取输出内容
        /// </summary>
        private static async Task<string> ReadResponseBody(HttpResponse response)
        {
            response.Body.Seek(0, SeekOrigin.Begin);

            if (response.ContentType == null || !response.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            {
                return "Response content is not in json format";
            }

            var text = await new StreamReader(response.Body).ReadToEndAsync();

            response.Body.Seek(0, SeekOrigin.Begin);

            return text;
        }

        public async Task Invoke(HttpContext context, IDiagnosticContext diagnosticContext)
        {
            if (context == null)
            {
                return;
            }

            var requestBody = await ReadRequestBody(context.Request);
            diagnosticContext.Set("RequestBody", requestBody);

            var originalBodyStream = context.Response.Body;
            using var responseStream = new MemoryStream();
            context.Response.Body = responseStream;

            await _next(context);

            var responseBody = await ReadResponseBody(context.Response);
            diagnosticContext.Set("ResponseBody", responseBody);
            await responseStream.CopyToAsync(originalBodyStream);
        }
    }

    public static class SerilogRequestBodyMiddlewareExtensions
    {
        public static IApplicationBuilder UseSerilogRequestLoggingPro(this IApplicationBuilder app)
        {
            app.UseSerilogRequestLogging(options =>
            {
                options.MessageTemplate = "HTTP {RequestId} {RequestHost} {IpAddress} {StatusCode} {Elapsed:0} {RequestMethod} {RequestPath} {RequestBody} {ResponseBody}";
            });
            // must after `app.UseSerilogRequestLogging()`
            return app.UseMiddleware<SerilogRequestLoggingMiddleware>();
        }
    }
}
